[Feat] #66 미지원 브랜드 QR 스캔 시 대응 기능 구현#67
Conversation
하나의 CTA 버튼과, 그 아래에 밑줄이 있는 텍스트 버튼을 포함하는 다이얼로그 `SingleButtonWithTextButtonAlertDialog`를 추가합니다.
개요QR 코드 스캔 시 지원하지 않는 브랜드 대응 로직을 구현했습니다. 다이얼로그 컴포넌트를 추가하고, 브랜드 지원 여부에 따른 조건부 흐름을 추가했으며, 갤러리 업로드 및 브랜드 제안 기능을 통합했습니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
actor User
participant QRImageAnalyzer as QRImageAnalyzer
participant QRScanViewModel as QRScanViewModel
participant QRScanScreen as QRScanScreen
participant Dialog as Dialog UI
User->>QRImageAnalyzer: QR 코드 스캔
QRImageAnalyzer->>QRScanViewModel: ScanQRCode(scannedUrl)
alt 지원하는 브랜드
QRScanViewModel->>QRScanViewModel: isSupportedBrand() 확인
alt 먼저 다운로드 필요
QRScanViewModel->>QRScanScreen: isShowShouldDownloadDialog = true
QRScanScreen->>Dialog: 다운로드 다이얼로그 표시
User->>Dialog: 다운로드 클릭
QRScanViewModel->>QRScanScreen: WEB_VIEW로 전환
else 바로 실행 가능
QRScanViewModel->>QRScanScreen: WEB_VIEW로 전환
end
else 지원하지 않는 브랜드
QRScanViewModel->>QRScanScreen: isShowUnSupportedBrandDialog = true
QRScanScreen->>Dialog: 지원 안 함 다이얼로그 표시
alt 갤러리 업로드
User->>Dialog: 갤러리 오픈 클릭
QRScanViewModel->>QRScanScreen: SetOpenGalleryResult
else 브랜드 제안
User->>Dialog: 제안하기 클릭
QRScanViewModel->>QRScanScreen: OpenBrandProposalUrl
end
end
예상 코드 리뷰 소요 시간🎯 4 (Complex) | ⏱️ ~45 minutes 관련 PR
제안 라벨
제안 리뷰어
축하 시
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@detekt-config.yml`:
- Around line 57-58: 현재 detekt 설정에서 FunctionOnlyReturningConstant 규칙을 전역
비활성화(active: false)한 대신 해당 규칙을 다시 활성화하고(삭제하거나 true로 변경) 상수만 반환하는 특정 함수들에
`@Suppress`("FunctionOnlyReturningConstant")를 적용하도록 변경하세요; 특히 Composable preview
함수나 예외가 필요한 함수들(예: `@Preview` 또는 미리보기 관련 함수명)에만 `@Suppress를` 추가하고, 이미 사용 중인
ignoreAnnotated 패턴이 있다면 동일한 방식으로 해당 주석을 허용하도록 detekt 설정에 반영하세요.
In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt`:
- Line 54: QRScannerContent 컴포저블 본문에서 직접 onQRCodeScanned("")를 호출하고 있으니 해당 호출을
제거하고 사이드 이펙트로 옮기세요: Composable 내부에서 onQRCodeScanned("") 라인(테스트/디버그 호출)을 삭제하고, 실제
초기 호출이 필요하면 QRScannerContent(…)의 상태/결과가 변경될 때만 실행되도록 LaunchedEffect 또는
rememberUpdatedState+SideEffect 패턴을 사용해 트리거하세요; 대상 심볼: onQRCodeScanned,
QRScannerContent(컴포저블) — 빈 문자열 직접 전달을 제거하고 실제 스캔 결과가 준비되었을 때만 콜백을 호출하도록 변경하세요.
In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanScreen.kt`:
- Around line 43-46: The OpenBrandProposalUrl handling currently calls
context.startActivity(...) directly and can crash if no browser can handle the
intent; in the QRScanSideEffect.OpenBrandProposalUrl branch, build the
Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BRAND_PROPOSAL_URL)) and call
intent.resolveActivity(context.packageManager) before startActivity; if
resolveActivity returns null, do not start the activity and instead show a safe
failure UI (e.g., Toast) to inform the user that no browser is available or the
link cannot be opened.
In
`@feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/QRScanViewModel.kt`:
- Around line 59-60: The ClickGoDownload intent handler in QRScanViewModel only
sets viewType but doesn't close the dialog, so update the
QRScanIntent.ClickGoDownload branch (where reduce { copy(viewType =
QRScanViewType.WEB_VIEW) } is used) to also set isShowShouldDownloadDialog =
false (e.g., reduce { copy(viewType = QRScanViewType.WEB_VIEW,
isShowShouldDownloadDialog = false) }) so the dialog is dismissed when
navigating to the download view.
🧹 Nitpick comments (4)
feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt (1)
48-57: 타입 안전한 QR 스캔 결과 처리로의 리팩토링이 잘 되었습니다.
String대신 sealed interfaceQRScanResult를 사용하여 타입 안전성을 높이고,when표현식을 통해 컴파일 타임에 모든 케이스를 처리하도록 보장한 점이 좋습니다.사소한 스타일 제안: Line 53의 빈 줄은
when블록 내에서 불필요해 보입니다. 제거하면 코드가 더 간결해집니다.,
🧹 선택적 스타일 개선
ResultEffect<QRScanResult>(resultBus) { result -> when (result) { is QRScanResult.QRCodeScanned -> { viewModel.store.onIntent(ArchiveMainIntent.QRCodeScanned(result.imageUrl)) } - QRScanResult.OpenGallery -> { viewModel.store.onIntent(ArchiveMainIntent.ClickGalleryUploadRow) } } }core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/DoubleButtonAlertDialog.kt (1)
40-42: DialogProperties 기본값이 다른 다이얼로그들과 불일치합니다.
SingleButtonAlertDialog와SingleButtonWithTextButtonAlertDialog는dismissOnBackPress = false,dismissOnClickOutside = false를 기본값으로 설정하지만, 이 다이얼로그는usePlatformDefaultWidth = false만 설정합니다.의도된 차이라면 무시해도 되지만, 일관성을 위해 확인이 필요합니다.
♻️ 일관성을 위한 제안
properties: DialogProperties = DialogProperties( usePlatformDefaultWidth = false, + dismissOnBackPress = false, + dismissOnClickOutside = false, ),core/designsystem/src/main/java/com/neki/android/core/designsystem/dialog/SingleButtonWithTextButtonAlertDialog.kt (1)
97-105: 접근성 개선을 위해 semantics 추가를 고려해 보세요.
clickableSingle수정자만으로는 스크린 리더가 이 요소를 버튼으로 인식하지 못할 수 있습니다.♻️ 접근성 개선 제안
+import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics ... Text( modifier = Modifier .padding(bottom = 8.dp) - .clickableSingle(onClick = onTextButtonClick), + .semantics { role = Role.Button } + .clickableSingle(onClick = onTextButtonClick), text = textButtonText, style = NekiTheme.typography.body14Regular, color = NekiTheme.colorScheme.primary600, textDecoration = TextDecoration.Underline, )feature/photo-upload/impl/build.gradle.kts (1)
23-23: BRAND_PROPOSAL_URL 누락 여부를 빌드 단계에서 보장해주세요.
local.properties/CI에 값이 없으면 BuildConfig에"null"문자열이 들어가 잘못된 URI가 될 수 있습니다. 존재 검증(없으면 빌드 실패)이나 기본값 처리 확인이 필요합니다.
Ojongseok
left a comment
There was a problem hiding this comment.
고생하셨습니다-!
디자인 시스템 내 다이어로그 컴포넌트에
properties: DialogProperties = DialogProperties(
usePlatformDefaultWidth = false,
),
해당 속성을 추가하자고 말씀을 드리려 했는데 변경해주셨군요. 제가 사용하고 있는 다이어로그에서 해당 속성 설정하고 있는 부분도 차차 제거하겠습니다.
| private fun isShouldFirstDownloadBrand(url: String): Boolean { | ||
| return false | ||
| } |
There was a problem hiding this comment.
다운로드를 해야 앱에 저장되는 브랜드 url을 구분하는 함수로 보이는데 이후 추가될 예정인가요?
함수명의 First라는 워딩이 어떤 의미인지 궁금합니다.
There was a problem hiding this comment.
다운로드를 해야 앱에 저장되는 브랜드 url을 구분하는 함수로 보이는데 이후 추가될 예정인가요?
네 맞습니다.
추후 다운로드 되어야하는 브랜드 URL 을 삽입하기만 하면 되도록 구현했습니다.
함수명의 First라는 워딩이 어떤 의미인지 궁금합니다.
먼저 저장되어야한다, 라는 의미로 작성했습니다. First라는 단어를 빼는 게 좋을까요?
There was a problem hiding this comment.
다운로드를 해야 하는 브랜드의 경우 '먼저 다운로드'의 개념 보다는 사실 '반드시 다운로드'해야 앱에도 저장되기 때문에 First를 제외한 isShouldDownloadBrand()의 네이밍이 좀 더 적절하지 않나 생각이 드네요!
"다운로드 하러가기" 버튼 클릭 시, `isShowShouldDownloadDialog`를 `false`로 설정하여 QR 코드 스캔 후 나타나는 다운로드 안내 팝업이 정상적으로 닫히도록 수정합니다.
`android.net.Uri`의 `parse()` static 메서드를 사용하는 대신, `androidx.core.net`의 `String.toUri()` 확장 함수를 사용하도록 수정합니다.
🔗 관련 이슈
📙 작업 설명
FunctionOnlyReturningConstant규칙 비활성화📷 스크린샷
default.mp4
QR.mp4
💬 추가 설명 or 리뷰 포인트 (선택)
QRScanResultSummary by CodeRabbit
릴리스 노트
새로운 기능
개선
✏️ Tip: You can customize this high-level summary in your review settings.